package function

import (
	"fmt"
	"reflect"
	"strings"
	"time"

	"gitee.com/knowgo/ego/egoc"
	"gitee.com/knowgo/ego/log"

	"gitee.com/knowgo/ego/core/types"
	"gitee.com/knowgo/ego/core/validator"
)

type ArgumentPool interface {
	GetArgumentType(typeName string) *ArgumentTypeInfo
}

type ArgumentObject struct {
	Type *types.TypeInfo
	Info *ArgumentTypeInfo
	log  log.Logger
}

// ParseArgument 调用之前要先判断是否注入类型
func ParseArgument(argTypes map[string]*ArgumentTypeInfo, typ reflect.Type, location string, log log.Logger, fnGetTimeormat func(format egoc.TimeFormatter) (string, *time.Location)) (*ArgumentObject, error) {
	var r = ArgumentObject{log: log}
	_, err := r.parse(argTypes, typ, location, true, fnGetTimeormat)
	return &r, err
}

func (r *ArgumentObject) IsArray() bool {
	return r.Type.IsSlice
}

// func (r *Return) Parse(typ reflect.Type, location string) error {
// 	var objSet = map[string]struct{}{}
// 	_, err := r.parse(objSet, typ, location, true)
// 	return err
// }

func (r *ArgumentObject) parse(argTypes map[string]*ArgumentTypeInfo, typ reflect.Type, location string, root bool, fnGetTimeormat func(format egoc.TimeFormatter) (string, *time.Location)) (*ArgumentTypeInfo, error) {
	typeInfo, err := types.ParseType(typ, location)
	if err != nil {
		return nil, err
	}

	// 解析
	info := &ArgumentTypeInfo{
		ID:       typeInfo.ID(),
		Type:     typeInfo.TypeName(),
		Time:     typeInfo.IsTime,
		Array:    typeInfo.IsSlice,
		Ptr:      typeInfo.IsPtr,
		KeyType:  typeInfo.KeyTypeName(),
		TypeInfo: typeInfo,
	}
	if root {
		r.Type, r.Info = typeInfo, info
	}

	if info.Array || info.KeyType != "" {
		// 数组或 map ，需要解析其存储元素对应的类型
		var typeId = ""
		if !typeInfo.IsReferencePrimitive {
			typeId = typeInfo.ID()
		}
		info.ItemType = &ArgumentTypeInfo{
			ID:       typeInfo.ID(),
			TypeId:   typeId,
			Type:     typeInfo.TypeName(),
			Time:     typeInfo.IsTime,
			Array:    false,
			Ptr:      typeInfo.IsPtr,
			TypeInfo: typeInfo,
		}
	}

	if typeInfo.IsReferenceStruct && !typeInfo.IsTime {
		// 是自定义结构体
		_, ok := argTypes[typeInfo.ID()]
		if ok {
			// 已经解析过，不再解析
			info.TypeId = typeInfo.ID() // 指向结构体 ID
			return info, nil
		}
		// }

		// // objPool[info.ID] = info
		// if typeInfo.IsReferenceStruct { // 指向类型是结构体
		// 添加到已经解析过的集合中
		// if !typeInfo.IsTime {
		// 添加到已解析集合
		argTypes[typeInfo.ID()] = info
		info.TypeId = typeInfo.ID()
		// }

		count := typeInfo.ReferenceType.NumField()

		for n := 0; n < count; n++ {
			field := typeInfo.ReferenceType.Field(n)
			if !field.IsExported() {
				continue
			}
			loc := fmt.Sprintf("%s\n\t%s.%s", location, typeInfo.TypeName(), field.Name)
			res, err := r.parse(argTypes, field.Type, loc, false, fnGetTimeormat)
			if err != nil {
				return nil, err
			}
			// 解析 tag，并获取描述信息
			tag := validator.ParseTag(&field, func(tagItem string) {
				r.log.Warnf("tag %s is ignored%s", tagItem, loc)
			})
			if tag == nil {
				// 忽略此字段 json:"-"
				continue
			}
			res.Description = strings.Join(tag.Descriptions, ";")
			res.Name = tag.JsonName
			res.FieldName = field.Name
			res.Required = tag.Required
			res.Deprecated = tag.Deprecated
			res.JsonValueIsString = tag.JsonValueIsString
			res.Validators = tag.Validators
			res.TimeFormat, res.TimeLocation = fnGetTimeormat(tag.DateTimeType)
			// res.TimeType = string(tag.DateTimeType)

			for n, v := range tag.Validators {
				if v.String() != "" {
					if n == 0 {
						res.Range += v.String()
					} else {
						res.Range += ";" + v.String()
					}
				}
			}

			// res.Name = fdTag.FieldName // json:"name"
			info.Fields = append(info.Fields, res)
		}
	}
	return info, nil
}
